using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Xml.Serialization;
using System.Text.RegularExpressions;

namespace WOWEquipOptimizer
{
    public enum AttributeName {None = 0, Agility, Intellect, Strength, Stamina, Spirit, CritRating, HitRating, CritMeleeRating, HitMeleeRating, HasteRating, HitSpellRating, CritSpellRating, HasteSpellRating, DodgeRating, ParryRating, BlockRating, BlockValue, DefenseRating, ResilienceRating, Healing, MP5, HP5, SpellDamage, FireSpellDamage, ArcaneSpellDamage, FrostSpellDamage, NatureSpellDamage, HolySpellDamage, ShadowSpellDamage, AttackPower, RangedAttackPower, SpellPenetration, Armor, Health, Mana, WeaponSkill, FeralAP, HasteDirect, ShadowResist, NatureResist, FrostResist, FireResist, ArcaneResist, ResistAll, Special, MHWDPS, MHWS, OHWDPS, OHWS };
    public enum WeaponType { Misc = -1, None = 0, Sword = 1, Mace = 2, Axe, FistWeapon, Dagger, Staff, Polearm, FishingPole, Thrown, Crossbow, Bow, Gun, Wand, Feral, Totem, Libram, Idol };
    [Flags]
    public enum ArmorProficiency { Junk = -1, Cloth = 1, Leather = 2, Mail = 4, Plate = 8, Shield = 16 };
    [Flags]
    public enum GemType { None = 0, Blue = 1, Yellow = 2, Red = 4, Meta = 8 }
    public enum ItemClass { Armor = 1, Trinket, Ring, Weapon, Gem };
    public enum Quality { None, Standard, Good, Superior, Epic, Legendary };
    [Flags]
    public enum SlotName { None = -1, Head = 0, Neck, Shoulder, Back, Chest, Wrist, Hands, Waist, Legs, Feet, Finger1, Finger2, Trinket1, Trinket2, MainHand, OffHand, Ranged };
    // none, createdByPlans, questReward, creatureDrop, createdBySpell, gameObjectDrop, vendor, pvpReward, worldDrop, factionReward, providedQuest
    public enum ItemSource { None = 0, Drop, Quest, Recipie, Vendor, PVP, Faction, Other };
    [Serializable()]
    public struct Set
    {
        public string Name;
        public List<string> Item;
        public List<Attribute> Bonus;
        public List<int> Treshold;
        public Set(string name)
        {
            Name = name;
            Item = new List<string>();
            Bonus = new List<Attribute>();
            Treshold = new List<int>();
        }
    }
    public struct Attribute
    {
        public AttributeName Type;
        public float Value;
        public Attribute(AttributeName type, float value)
        {
            Type = type;
            Value = value;
        }
        public static Attribute operator +(Attribute a, Attribute b)
        {
            if (a.Type == b.Type) return new Attribute(a.Type, a.Value + b.Value);
            else throw new Exception("Unsupported Operation");
        }
    }
    [Serializable()]
    public struct WeaponSkill
    {
        public WeaponType Weapon;
        public float Value;
        public WeaponSkill(WeaponType weapon, float value)
        {
            Weapon = weapon;
            Value = value;
        }
    }
    [Serializable()]
    [XmlInclude(typeof(Armor)), XmlInclude(typeof(Gem)), XmlInclude(typeof(WeaponSkill)), XmlInclude(typeof(Ring)), XmlInclude(typeof(Trinket)), XmlInclude(typeof(Weapon))]
    public abstract partial class Item : ICloneable
    {
        protected ulong m_Hash = 0;
        protected Quality m_Quality = Quality.None;

        public Quality Quality
        {
            get { return m_Quality; }
            set { m_Quality = value; }
        }
        protected int m_Id;

        public virtual int Id
        {
            get { return m_Id; }
            set
            {
                m_Id = value;
            }
        }
        protected string m_Name;

        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }
        protected string m_Icon;

        public string Icon
        {
            get { return m_Icon; }
            set { m_Icon = value; }
        }
        protected int m_Level;

        public int Level
        {
            get { return m_Level; }
            set { m_Level = value; }
        }
        protected ItemSource m_Source;
        public ItemSource Source
        {
            get { return m_Source; }
            set { m_Source = value; }
        }
        protected List<string> m_CreatureName = new List<string>();

        public List<string> CreatureName
        {
            get { return m_CreatureName; }
            set { m_CreatureName = value; }
        }
        protected List<string> m_AreaName = new List<string>();
        public List<string> AreaName
        {
            get { return m_AreaName; }
            set { m_AreaName = value; }
        }
        protected int m_Armor;

        public int Armor
        {
            get { return m_Armor; }
            set { m_Armor = value; }
        }
        protected bool m_Unique;

        public bool Unique
        {
            get { return m_Unique; }
            set { m_Unique = value; }
        }
        protected CharacterClass m_AllowableClasses = CharacterClass.Druid | CharacterClass.Hunter | CharacterClass.Mage | CharacterClass.Paladin | CharacterClass.Priest | CharacterClass.Rogue | CharacterClass.Shaman | CharacterClass.Warlock | CharacterClass.Warrior;

        public CharacterClass AllowableClasses
        {
            get { return m_AllowableClasses; }
            set { m_AllowableClasses = value; }
        }
        protected Set m_Set = new Set();

        public Set Set
        {
            get { return m_Set; }
            set { m_Set = value; }
        }
        protected List<Attribute> m_Attributes = new List<Attribute>();

        public List<Attribute> Attributes
        {
            get { return m_Attributes; }
            set { m_Attributes = value; }
        }
        private int m_SocketCount = 0;

        public int SocketCount
        {
            get { return m_SocketCount; }
            set { m_SocketCount = value; }
        }
        private GemType[] m_GemColors;

        public GemType[] GemColors
        {
            get { return m_GemColors; }
            set { m_GemColors = value; }
        }
        private Gem[] m_Gems;

        public Gem[] Gems
        {
            get { return m_Gems; }
            set
            {
                m_Gems = value;
                m_Hash = 0;
            }
        }
        private List<Attribute> m_GemBonus;

        public List<Attribute> GemBonus
        {
            get { return m_GemBonus; }
            set { m_GemBonus = value; }
        }
        private List<WeaponSkill> m_WeaponSkills = new List<WeaponSkill>();

        public List<WeaponSkill> WeaponSkills
        {
            get { return m_WeaponSkills; }
            set { m_WeaponSkills = value; }
        }
        [NonSerialized()]
        private Enchant m_Enchant = null;

        public Enchant Enchant
        {
            get { return m_Enchant; }
            set
            {
                m_Enchant = value;
                m_Hash = 0;
            }
        }
        public Item()
        {
            m_Name = "None";
            m_Id = 99999;
        }
        public new string ToString()
        {
            string add = string.Empty;
            if (m_SocketCount > 0)
            {
                add = " Sockets: ";
                for (int i = 0; i < Gems.Length; i++)
                {
                    if (Gems[i] != null) add += Gems[i].Name + ", ";
                }
            }
            return m_Name + add;
        }
        public int getItemLevel()
        {
            double power = Math.Log(2) / Math.Log(1.5f);
            double slotmod = 0;
            double qualitymod = 0;
            if (this.GetType() == typeof(Armor))
            {
                Armor armor = (Armor)this;
                switch (armor.Slot)
                {
                    case ArmorSlot.Head:
                    case ArmorSlot.Chest:
                    case ArmorSlot.Legs:
                        {
                            slotmod = 1.0;
                            break;
                        }
                    case ArmorSlot.Shoulder:
                    case ArmorSlot.Hands:
                    case ArmorSlot.Waist:
                    case ArmorSlot.Feet:
                        {
                            slotmod = 1.29;
                            break;
                        }
                    case ArmorSlot.Wrist:
                    case ArmorSlot.Neck:
                    case ArmorSlot.Back:
                    case ArmorSlot.OffHand:
                    case ArmorSlot.Shield:
                        {
                            slotmod = 1.82;
                            break;
                        }
                    default:
                        break;
                }
            }
            else if (this.GetType() == typeof(Weapon))
            {
                if ((this as Weapon).TwoHand) slotmod = 1.0;
                else if ((this as Weapon).WeaponSlot == WeaponSlot.Ranged) slotmod = 3.33;
                else slotmod = 2.38;

            }
            else if (this.GetType() == typeof(Ring))
            {
                slotmod = 1.82;
            }
            else if (this.GetType() == typeof(Trinket))
            {
                slotmod = 1.43;
            }
            if (this.Quality == Quality.Epic) qualitymod = -34;
            else if (this.Quality == Quality.Superior) qualitymod = -12;
            else if (this.Quality == Quality.Good) qualitymod = 7.5;

            double itemvalue = Math.Pow((this.Armor * 0.1f), power);
            foreach (Attribute attr in Attributes)
            {
                switch (attr.Type)
                {
                    case AttributeName.AttackPower:
                        {
                            itemvalue += Math.Pow(attr.Value * 0.5f, power);
                            break;
                        }
                    case AttributeName.Healing:
                        {
                            itemvalue += Math.Pow(attr.Value * 5 / 11f, power);
                            break;
                        }
                    case AttributeName.BlockValue:
                        {
                            itemvalue += Math.Pow(attr.Value * 0.65f, power);
                            break;
                        }
                    case AttributeName.SpellDamage:
                        {
                            itemvalue += Math.Pow(attr.Value * 0.89f, power);
                            break;
                        }
                    case AttributeName.SpellPenetration:
                        {
                            itemvalue += Math.Pow(attr.Value * 0.8f, power);
                            break;
                        }
                    case AttributeName.Stamina:
                        {
                            itemvalue += Math.Pow(attr.Value * 2 / 3f, power);
                            break;
                        }
                    case AttributeName.HP5:
                    case AttributeName.MP5:
                        {
                            itemvalue += Math.Pow(attr.Value * 5 / 2f, power);
                            break;
                        }

                    default:
                        {
                            itemvalue += Math.Pow(attr.Value, power);
                            break;
                        }
                }
            }
            itemvalue = Math.Pow(itemvalue, 1 / power) * slotmod * 2.0f + qualitymod;
            return (int)Math.Floor(itemvalue);
        }
        public ulong GetMyHashCode()
        {
            ulong retval = (ulong)m_Id;
            int id = 0;
            if (m_Gems != null)
            {
                foreach (Gem gem in m_Gems)
                {
                    id = (gem == null) ? Gem.None.Id : gem.Id;
                    retval = (retval * (ulong)id) % 4273992419;
                }
            }
            id = (m_Enchant == null) ? Enchant.None.Id : Enchant.Id;
            retval = (retval * (ulong)id) % 4273992419;
            return retval;
        }
        public static int NoneId = 99999;
        public static ulong GetHashCode(Item[] array)
        {
            ulong retval = 1;
            for (int i = 0; i < array.Length; i++)
            {
                retval = (retval % 4273992419  * array[i].GetMyHashCode());
            }
            return retval;
        }
        public static Item getPlaceholderItem(int slot)
        {
            switch (slot)
            {
                case 10:
                case 11:
                    return Ring.None;
                case 12:
                case 13:
                    return Trinket.None;
                case 14:
                case 15:
                case 16:
                    return Weapon.None;
                case 17:
                    return Gem.None;
                default:
                    return WOWEquipOptimizer.Armor.None;
            }
        }
        [NonSerialized()]
        public static string CmpString = string.Empty;
        public static bool FindString(Item item)
        {
            if (item.Name == CmpString) return true;
            return false;
        }
        #region ICloneable Member

        public object Clone()
        {
            return this.MemberwiseClone();
        }

        #endregion
    }
}
